#include "Avalon/ServiceLocatorTests/ServiceLocatorServerTester.hpp"
#include <boost/lexical_cast.hpp>
#include "Avalon/IOTests/MockServerConnection.hpp"
#include "Avalon/Serialization/BinaryDataShuttleFactory.hpp"
#include "Avalon/ServiceLocator/DirectoryEntry.hpp"
#include "Avalon/ServiceLocator/ServiceLocatorClient.hpp"
#include "Avalon/ServiceLocator/ServiceLocatorServices.hpp"
#include "Avalon/ServiceLocatorTests/MockServiceLocatorDataStore.hpp"

using namespace Avalon;
using namespace Avalon::IO;
using namespace Avalon::IO::Tests;
using namespace Avalon::Serialization;
using namespace Avalon::ServiceLocator;
using namespace Avalon::ServiceLocator::Tests;
using namespace Avalon::Services;
using namespace Avalon::SignalHandling;
using namespace Avalon::Threading;
using namespace boost;
using namespace boost::posix_time;
using namespace std;

void ServiceLocatorServerTester::setUp() {
  m_serverConnection = new MockServerConnection();
  m_dataStore = new MockServiceLocatorDataStore();
  m_server.reset(new ServiceLocatorServer(m_dataStore, m_serverConnection,
    new BinaryDataShuttleFactory(), pos_infin));
  CPPUNIT_ASSERT_NO_THROW(m_server->Start());
  m_channel.reset(new MockClientChannel(m_serverConnection));
  CPPUNIT_ASSERT_NO_THROW(static_cast<ClientConnection&>(
    m_channel->GetConnection()).Connect());
  m_service.reset(new ServiceNode(new BinaryDataShuttleFactory(), pos_infin));
  bool isOpen;
  m_service->AddChannel(m_channel.get(),
    &NullSlot<Connection::ClosedSignal>::Slot, &isOpen);
}

void ServiceLocatorServerTester::tearDown() {
  m_service->CloseAndRemoveChannel(m_channel.get());
  m_service.reset();
  m_channel.reset();
  m_server.reset();
}

void ServiceLocatorServerTester::TestLoginWithInvalidAccount() {
  LoginService::ReturnType result = m_service->SendRequest<LoginService>(
    m_channel.get(), "invalid_username", "")->Get(pos_infin);
  CPPUNIT_ASSERT(!result.get<0>());
  CPPUNIT_ASSERT(result.get<2>() == DirectoryEntry::Tag());
  CPPUNIT_ASSERT(result.get<3>().empty());
}

void ServiceLocatorServerTester::TestLoginWithInvalidPassword() {
  string username = "user1";
  string password = "password";
  CreateUser(username, password);
  LoginService::ReturnType result = m_service->SendRequest<LoginService>(
    m_channel.get(), username, "1234")->Get(pos_infin);
  CPPUNIT_ASSERT(!result.get<0>());
  CPPUNIT_ASSERT(result.get<2>() == DirectoryEntry::Tag());
  CPPUNIT_ASSERT(result.get<3>().empty());
}

void ServiceLocatorServerTester::TestValidLogin() {
  string username = "user1";
  string password = "password";
  CreateUser(username, password);
  LoginService::ReturnType result = m_service->SendRequest<LoginService>(
    m_channel.get(), username, password)->Get(pos_infin);
  CPPUNIT_ASSERT(result.get<0>());
  CPPUNIT_ASSERT(result.get<2>().m_name == username);
  CPPUNIT_ASSERT(result.get<3>().size() == SESSION_ID_LENGTH);

  // Test logging in again, duplicate login.
  result = m_service->SendRequest<LoginService>(m_channel.get(), username,
    password)->Get(pos_infin);
  CPPUNIT_ASSERT(!result.get<0>());
  CPPUNIT_ASSERT(result.get<3>().empty());
}

void ServiceLocatorServerTester::TestSessionAuthenticationWithoutLogin() {
  SessionAuthenticationService::ReturnType result =
    m_service->SendRequest<SessionAuthenticationService>(m_channel.get(), "",
    0)->Get(pos_infin);
  CPPUNIT_ASSERT(!result.get<0>());
  CPPUNIT_ASSERT(result.get<2>().m_id == -1);
}

void ServiceLocatorServerTester::
    TestSessionAuthenticationWithInvalidSessionId() {
  CreateAccountAndLogin();
  SessionAuthenticationService::ReturnType result =
    m_service->SendRequest<SessionAuthenticationService>(m_channel.get(), "",
    0)->Get(pos_infin);
  CPPUNIT_ASSERT(!result.get<0>());
  CPPUNIT_ASSERT(result.get<2>().m_id == -1);
}

void ServiceLocatorServerTester::TestValidSessionAuthentication() {
  pair<DirectoryEntry::Tag, string> sessionInfo = CreateAccountAndLogin();
  unsigned int key = 0;
  string encodedSessionId = ComputeSHA("0" + sessionInfo.second);
  SessionAuthenticationService::ReturnType result =
    m_service->SendRequest<SessionAuthenticationService>(m_channel.get(),
    encodedSessionId, key)->Get(pos_infin);
  CPPUNIT_ASSERT(result.get<0>());
  CPPUNIT_ASSERT(result.get<2>() == sessionInfo.first);
}

void ServiceLocatorServerTester::TestExpiredSessionAuthentication() {
  CreateAccountAndLogin();
  scoped_ptr<ServiceNode> expiredService;
  scoped_ptr<Channel> expiredChannel;
  pair<DirectoryEntry::Tag, string> expiredSessionInfo =
    CreateAdditionalClient(&expiredService, &expiredChannel, "user2",
    "password");
  unsigned int key = 0;
  string encodedSessionId = ComputeSHA("0" + expiredSessionInfo.second);
  expiredService->CloseAndRemoveChannel(expiredChannel.get());
  SessionAuthenticationService::ReturnType result =
    m_service->SendRequest<SessionAuthenticationService>(m_channel.get(),
    encodedSessionId, key)->Get(pos_infin);
  CPPUNIT_ASSERT(!result.get<0>());
  CPPUNIT_ASSERT(result.get<2>().m_id == -1);
}

void ServiceLocatorServerTester::TestRegisterServiceWithoutLogin() {
  string service = "service";
  string details = "tcp://localhost";
  RegisterService::ReturnType result = m_service->SendRequest<RegisterService>(
    m_channel.get(), service, details)->Get(pos_infin);
  CPPUNIT_ASSERT(!result.get<0>());
}

void ServiceLocatorServerTester::TestRegisterService() {
  string service = "service";
  string details = "tcp://localhost";
  CreateAccountAndLogin();
  RegisterService::ReturnType result = m_service->SendRequest<RegisterService>(
    m_channel.get(), service, details)->Get(pos_infin);
  CPPUNIT_ASSERT(result.get<0>());
  CPPUNIT_ASSERT(result.get<2>().GetName() == service);
  CPPUNIT_ASSERT(result.get<2>().GetDetails() == details);
  CPPUNIT_ASSERT(result.get<2>().GetId() == 1);
}

void ServiceLocatorServerTester::TestUnregisterServiceWithoutLogin() {
  UnregisterService::ReturnType result =
    m_service->SendRequest<UnregisterService>(m_channel.get(),
    1)->Get(pos_infin);
  CPPUNIT_ASSERT(!result.get<0>());
}

void ServiceLocatorServerTester::TestUnregisterInvalidService() {
  CreateAccountAndLogin();
  UnregisterService::ReturnType result =
    m_service->SendRequest<UnregisterService>(m_channel.get(),
    1)->Get(pos_infin);
  CPPUNIT_ASSERT(!result.get<0>());
}

void ServiceLocatorServerTester::TestRegisterUnregisterService() {
  string service = "service";
  string details = "tcp://localhost";
  int serviceId = 1;

  // Login and register the service.
  CreateAccountAndLogin();
  RegisterService::ReturnType registerResult =
    m_service->SendRequest<RegisterService>(m_channel.get(),
    service, details)->Get(pos_infin);
  CPPUNIT_ASSERT(registerResult.get<0>());
  CPPUNIT_ASSERT(registerResult.get<2>().GetName() == service);
  CPPUNIT_ASSERT(registerResult.get<2>().GetDetails() == details);
  CPPUNIT_ASSERT(registerResult.get<2>().GetId() == serviceId);

  // Unregister the service.
  UnregisterService::ReturnType unregisterResult =
    m_service->SendRequest<UnregisterService>(m_channel.get(),
    serviceId)->Get(pos_infin);
  CPPUNIT_ASSERT(unregisterResult.get<0>());

  // Re-register the service.
  RegisterService::ReturnType reregisterResult =
    m_service->SendRequest<RegisterService>(m_channel.get(),
    service, details)->Get(pos_infin);
  CPPUNIT_ASSERT(reregisterResult.get<0>());
  CPPUNIT_ASSERT(reregisterResult.get<2>().GetName() == service);
  CPPUNIT_ASSERT(reregisterResult.get<2>().GetDetails() == details);
  CPPUNIT_ASSERT(reregisterResult.get<2>().GetId() == 2);
}

void ServiceLocatorServerTester::TestLocatingWithoutLogin() {
  LocateService::ReturnType result = m_service->SendRequest<LocateService>(
    m_channel.get(), "")->Get(pos_infin);
  CPPUNIT_ASSERT(!result.get<0>());
}

void ServiceLocatorServerTester::TestLocatingNonExistingService() {
  CreateAccountAndLogin();
  LocateService::ReturnType result = m_service->SendRequest<LocateService>(
    m_channel.get(), "")->Get(pos_infin);
  CPPUNIT_ASSERT(result.get<0>());
  CPPUNIT_ASSERT(result.get<2>().empty());
}

void ServiceLocatorServerTester::TestLocatingSingleProviderService() {
  CreateAccountAndLogin();
  scoped_ptr<Channel> providerChannel;
  scoped_ptr<ServiceNode> providerService;
  DirectoryEntry::Tag provider = CreateAdditionalClient(&providerService,
    &providerChannel, "user2", "password").first;

  // Register the service using the mock provider.
  RegisterService::ReturnType registerResult =
    providerService->SendRequest<RegisterService>(providerChannel.get(),
    "service", "details")->Get(pos_infin);
  CPPUNIT_ASSERT(registerResult.get<0>());

  // Locate the service using the mock client.
  LocateService::ReturnType locateResult =
    m_service->SendRequest<LocateService>(m_channel.get(), "service")->Get(
    pos_infin);
  CPPUNIT_ASSERT(locateResult.get<0>());
  const vector<ServiceEntry>& services = locateResult.get<2>();
  CPPUNIT_ASSERT(services.size() == 1);
  ServiceEntry replyService = services[0];
  CPPUNIT_ASSERT(replyService.GetName() == "service");
  CPPUNIT_ASSERT(replyService.GetId() == 1);
  CPPUNIT_ASSERT(replyService.GetAccount().m_name == provider.m_name);
  CPPUNIT_ASSERT(replyService.GetAccount().m_id == provider.m_id);

  // Unregister the service.
  UnregisterService::ReturnType unregisterResult =
    providerService->SendRequest<UnregisterService>(providerChannel.get(),
    replyService.GetId())->Get(pos_infin);
  CPPUNIT_ASSERT(unregisterResult.get<0>());

  // Redo the locate.
  locateResult = m_service->SendRequest<LocateService>(m_channel.get(),
    "service")->Get(pos_infin);
  CPPUNIT_ASSERT(locateResult.get<0>());
  CPPUNIT_ASSERT(locateResult.get<2>().empty());
  providerService->CloseAndRemoveChannel(providerChannel.get());
}

void ServiceLocatorServerTester::TestLocatingMultipleProviderService() {
  string service = "service";
  string details = "tcp://localhost";
  CreateAccountAndLogin();
  scoped_ptr<Channel> providerChannel;
  scoped_ptr<ServiceNode> providerService;
  DirectoryEntry::Tag firstProvider = CreateAdditionalClient(&providerService,
    &providerChannel, "user2", "password").first;

  // Register the service using the first provider.
  int firstServiceId = 1;
  int secondServiceId = 2;
  RegisterService::ReturnType registerResult =
    providerService->SendRequest<RegisterService>(providerChannel.get(),
    service, details)->Get(pos_infin);
  CPPUNIT_ASSERT(registerResult.get<0>());
  CPPUNIT_ASSERT(registerResult.get<2>().GetName() == service);
  CPPUNIT_ASSERT(registerResult.get<2>().GetDetails() == details);
  CPPUNIT_ASSERT(registerResult.get<2>().GetId() == 1);

  // Register the service using the second provider.
  scoped_ptr<Channel> secondaryProviderChannel;
  scoped_ptr<ServiceNode> secondaryProviderService;
  DirectoryEntry::Tag secondProvider = CreateAdditionalClient(
    &secondaryProviderService, &secondaryProviderChannel, "user3",
    "password").first;
  registerResult = secondaryProviderService->SendRequest<RegisterService>(
    secondaryProviderChannel.get(), service, details)->Get(pos_infin);
  CPPUNIT_ASSERT(registerResult.get<0>());
  CPPUNIT_ASSERT(registerResult.get<2>().GetName() == service);
  CPPUNIT_ASSERT(registerResult.get<2>().GetDetails() == details);
  CPPUNIT_ASSERT(registerResult.get<2>().GetId() == 2);

  // Locate the service using the mock client.
  LocateService::ReturnType locateResult =
    m_service->SendRequest<LocateService>(m_channel.get(), "service")->Get(
    pos_infin);
  CPPUNIT_ASSERT(locateResult.get<0>());
  const vector<ServiceEntry>& services = locateResult.get<2>();
  CPPUNIT_ASSERT(services.size() == 2);
  ServiceEntry replyService = services[0];
  CPPUNIT_ASSERT(replyService.GetName() == service);
  CPPUNIT_ASSERT(replyService.GetDetails() == details);
  CPPUNIT_ASSERT(replyService.GetId() == 1);
  CPPUNIT_ASSERT(replyService.GetAccount().m_name == firstProvider.m_name);
  CPPUNIT_ASSERT(replyService.GetAccount().m_id == firstProvider.m_id);
  replyService = services[1];
  CPPUNIT_ASSERT(replyService.GetName() == service);
  CPPUNIT_ASSERT(replyService.GetDetails() == details);
  CPPUNIT_ASSERT(replyService.GetId() == 2);
  CPPUNIT_ASSERT(replyService.GetAccount().m_name == secondProvider.m_name);
  CPPUNIT_ASSERT(replyService.GetAccount().m_id == secondProvider.m_id);
  providerService->CloseAndRemoveChannel(providerChannel.get());
  secondaryProviderService->CloseAndRemoveChannel(
    secondaryProviderChannel.get());
}

void ServiceLocatorServerTester::TestCreateAccountWithoutLogin() {
  DirectoryEntry::Tag directory(DirectoryEntry::Type::DIRECTORY, 0, "*");
  CreateAccountService::ReturnType result =
    m_service->SendRequest<CreateAccountService>(m_channel.get(), "new_account",
    directory, "password")->Get(pos_infin);
  CPPUNIT_ASSERT(!result.get<0>());
}

void ServiceLocatorServerTester::TestCreateAccountWithoutPermissions() {
  DirectoryEntry::Tag account = CreateAccountAndLogin().first;
  DirectoryEntry::Tag directory(DirectoryEntry::Type::DIRECTORY, 0, "*");
  CreateAccountService::ReturnType result =
    m_service->SendRequest<CreateAccountService>(m_channel.get(), "new_account",
    directory, "password")->Get(pos_infin);
  CPPUNIT_ASSERT(!result.get<0>());

  // Check that read only permission also results in an error.
  m_dataStore->SetPermissions(account.m_id, directory.m_id,
    Directory::Permission::READ);
  result = m_service->SendRequest<CreateAccountService>(m_channel.get(),
    "new_account", directory, "password")->Get(pos_infin);
  CPPUNIT_ASSERT(!result.get<0>());
}

void ServiceLocatorServerTester::TestCreateAccountUnavailableName() {
  DirectoryEntry::Tag account = CreateAccountAndLogin().first;
  DirectoryEntry::Tag directory(DirectoryEntry::Type::DIRECTORY, 0, "*");
  m_dataStore->SetPermissions(account.m_id, directory.m_id,
    Directory::Permission::WRITE);
  string unavailableName = "unavailable";
  m_dataStore->AddUnavailableName(unavailableName);
  CreateAccountService::ReturnType result =
    m_service->SendRequest<CreateAccountService>(m_channel.get(),
    unavailableName, directory, "password")->Get(pos_infin);
  CPPUNIT_ASSERT(!result.get<0>());
}

void ServiceLocatorServerTester::TestValidCreateAccount() {
  DirectoryEntry::Tag account = CreateAccountAndLogin().first;
  DirectoryEntry::Tag directory(DirectoryEntry::Type::DIRECTORY, 0, "*");
  m_dataStore->SetPermissions(account.m_id, directory.m_id,
    Directory::Permission::WRITE);
  string accountName = "account";
  CreateAccountService::ReturnType result =
    m_service->SendRequest<CreateAccountService>(m_channel.get(), accountName,
    directory, "password")->Get(pos_infin);
  CPPUNIT_ASSERT(result.get<0>());
  CPPUNIT_ASSERT(!m_dataStore->IsNameAvailable(accountName));
  DirectoryEntry::Tag createdAccount = m_dataStore->LoadAccountTag(accountName);
  CPPUNIT_ASSERT(createdAccount.m_type == DirectoryEntry::Type::ACCOUNT);
  CPPUNIT_ASSERT(createdAccount.m_name == accountName);
}

void ServiceLocatorServerTester::TestCreateDirectoryWithoutLogin() {
  DirectoryEntry::Tag directory(DirectoryEntry::Type::DIRECTORY, 0, "*");
  CreateDirectoryService::ReturnType result =
    m_service->SendRequest<CreateDirectoryService>(m_channel.get(), "directory",
    directory)->Get(pos_infin);
  CPPUNIT_ASSERT(!result.get<0>());
}

void ServiceLocatorServerTester::TestCreateDirectoryWithoutPermissions() {
  DirectoryEntry::Tag account = CreateAccountAndLogin().first;
  DirectoryEntry::Tag directory(DirectoryEntry::Type::DIRECTORY, 0, "*");
  CreateDirectoryService::ReturnType result =
    m_service->SendRequest<CreateDirectoryService>(m_channel.get(), "directory",
    directory)->Get(pos_infin);
  CPPUNIT_ASSERT(!result.get<0>());

  // Check that read only permission also results in an error.
  m_dataStore->SetPermissions(account.m_id, directory.m_id,
    Directory::Permission::READ);
  result = m_service->SendRequest<CreateDirectoryService>(m_channel.get(),
    "directory", directory)->Get(pos_infin);
  CPPUNIT_ASSERT(!result.get<0>());
}

void ServiceLocatorServerTester::TestValidCreateDirectory() {
  DirectoryEntry::Tag account = CreateAccountAndLogin().first;
  DirectoryEntry::Tag directory(DirectoryEntry::Type::DIRECTORY, 0, "*");
  m_dataStore->SetPermissions(account.m_id, directory.m_id,
    Directory::Permission::WRITE);
  string directoryName = "directory";
  CreateDirectoryService::ReturnType result =
    m_service->SendRequest<CreateDirectoryService>(m_channel.get(),
    directoryName, directory)->Get(pos_infin);
  CPPUNIT_ASSERT(result.get<0>());
  CPPUNIT_ASSERT(m_dataStore->IsNameAvailable(directoryName));
}

void ServiceLocatorServerTester::TestDeleteEntryWithoutLogin() {
  DirectoryEntry::Tag entry(DirectoryEntry::Type::DIRECTORY, 0, "");
  DeleteEntryService::ReturnType result =
    m_service->SendRequest<DeleteEntryService>(m_channel.get(),
    entry.m_id)->Get(pos_infin);
  CPPUNIT_ASSERT(!result.get<0>());
}

void ServiceLocatorServerTester::TestDeleteNonExistingEntry() {
  CreateAccountAndLogin();
  DirectoryEntry::Tag entry(DirectoryEntry::Type::DIRECTORY, 1000, "");
  DeleteEntryService::ReturnType result =
    m_service->SendRequest<DeleteEntryService>(m_channel.get(),
    entry.m_id)->Get(pos_infin);
  CPPUNIT_ASSERT(!result.get<0>());
}

void ServiceLocatorServerTester::TestDeleteAccountWithoutPermissions() {
  CreateAccountAndLogin();
  DirectoryEntry::Tag directory(DirectoryEntry::Type::DIRECTORY, 0, "*");
  DirectoryEntry::Tag deletedAccount(DirectoryEntry::Type::ACCOUNT, 0,
    "deleted_account");
  deletedAccount.m_id = m_dataStore->CreateAccount(deletedAccount.m_name,
    directory);
  DeleteEntryService::ReturnType result =
    m_service->SendRequest<DeleteEntryService>(m_channel.get(),
    deletedAccount.m_id)->Get(pos_infin);
  CPPUNIT_ASSERT(!result.get<0>());
  CPPUNIT_ASSERT(m_dataStore->LoadTag(deletedAccount.m_id) == deletedAccount);
}

void ServiceLocatorServerTester::TestDeleteAccount() {
  DirectoryEntry::Tag account = CreateAccountAndLogin().first;
  DirectoryEntry::Tag directory(DirectoryEntry::Type::DIRECTORY, 0, "*");
  m_dataStore->SetPermissions(account.m_id, directory.m_id,
    Directory::Permission::WRITE);
  DirectoryEntry::Tag deletedAccount(DirectoryEntry::Type::ACCOUNT, 0,
    "deleted_account");
  deletedAccount.m_id = m_dataStore->CreateAccount(deletedAccount.m_name,
    directory);
  DeleteEntryService::ReturnType result =
    m_service->SendRequest<DeleteEntryService>(m_channel.get(),
    deletedAccount.m_id)->Get(pos_infin);
  CPPUNIT_ASSERT(result.get<0>());
  CPPUNIT_ASSERT(m_dataStore->LoadTag(deletedAccount.m_id) ==
    DirectoryEntry::Tag());
}

void ServiceLocatorServerTester::TestDeleteDirectoryWithoutPermissions() {
  CreateAccountAndLogin();
  DirectoryEntry::Tag directory(DirectoryEntry::Type::DIRECTORY, 0, "*");
  DirectoryEntry::Tag deletedDirectory(DirectoryEntry::Type::DIRECTORY, 0,
    "deleted_directory");
  deletedDirectory.m_id = m_dataStore->CreateDirectory(deletedDirectory.m_name,
    directory);
  DeleteEntryService::ReturnType result =
    m_service->SendRequest<DeleteEntryService>(m_channel.get(),
    deletedDirectory.m_id)->Get(pos_infin);
  CPPUNIT_ASSERT(!result.get<0>());
  CPPUNIT_ASSERT(m_dataStore->LoadTag(deletedDirectory.m_id) ==
    deletedDirectory);
}

void ServiceLocatorServerTester::TestDeleteDirectory() {
  DirectoryEntry::Tag account = CreateAccountAndLogin().first;
  DirectoryEntry::Tag directory(DirectoryEntry::Type::DIRECTORY, 0, "*");
  m_dataStore->SetPermissions(account.m_id, directory.m_id,
    Directory::Permission::WRITE);
  DirectoryEntry::Tag deletedDirectory(DirectoryEntry::Type::DIRECTORY, 0,
    "deleted_directory");
  deletedDirectory.m_id = m_dataStore->CreateDirectory(deletedDirectory.m_name,
    directory);
  DeleteEntryService::ReturnType result =
    m_service->SendRequest<DeleteEntryService>(m_channel.get(),
    deletedDirectory.m_id)->Get(pos_infin);
  CPPUNIT_ASSERT(result.get<0>());
  CPPUNIT_ASSERT(m_dataStore->LoadTag(deletedDirectory.m_id) ==
    DirectoryEntry::Tag());
}

void ServiceLocatorServerTester::CreateUser(const string& username,
    const string& password) {
  unsigned int accoundId = m_dataStore->CreateAccount(username,
    DirectoryEntry::Tag(DirectoryEntry::Type::DIRECTORY, 0, ""));
  CPPUNIT_ASSERT(accoundId != -1);
  CPPUNIT_ASSERT_NO_THROW(m_dataStore->SetPassword(accoundId,
    ComputeSHA(lexical_cast<string>(accoundId) + password)));
}

pair<DirectoryEntry::Tag, string>
    ServiceLocatorServerTester::CreateAccountAndLogin() {
  string username = "user1";
  string password = "password";
  CreateUser(username, password);
  LoginService::ReturnType result =
    m_service->SendRequest<LoginService>(m_channel.get(), username,
    password)->Get(pos_infin);
  CPPUNIT_ASSERT(result.get<0>());
  CPPUNIT_ASSERT(result.get<2>().m_name == username);
  return make_pair(result.get<2>(), result.get<3>());
}

pair<DirectoryEntry::Tag, string>
    ServiceLocatorServerTester::CreateAdditionalClient(
    scoped_ptr<ServiceNode>* service, scoped_ptr<Channel>* channel,
    const string& username, const string& password) {
  channel->reset(new MockClientChannel(m_serverConnection));
  CPPUNIT_ASSERT_NO_THROW(static_cast<ClientConnection&>(
    (*channel)->GetConnection()).Connect());
  service->reset(new ServiceNode(new BinaryDataShuttleFactory(), pos_infin));
  bool isOpen;
  (*service)->AddChannel(channel->get(),
    &NullSlot<Connection::ClosedSignal>::Slot, &isOpen);
  CreateUser(username, password);
  LoginService::ReturnType result =
    (*service)->SendRequest<LoginService>(channel->get(), username,
    password)->Get(pos_infin);
  CPPUNIT_ASSERT(result.get<0>());
  return make_pair(result.get<2>(), result.get<3>());
}
